package com.ocean.web.util;

import ucar.ma2.Array;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * 中国近海范围裁剪NetCDF数据工具类（适配4维数据[time, depth, lat, lon]）
 */
public class CpjypgSWT {
    // 中国近海目标经纬度范围（可根据需求修改）
    private static final double MIN_LAT = 14.0;   // 最小纬度（14°N）
    private static final double MAX_LAT = 42.0;   // 最大纬度（42°N）
    private static final double MIN_LON = 105.0;  // 最小经度（105°E）
    private static final double MAX_LON = 132.0;  // 最大经度（132°E）

    /**
     * 主方法：测试裁剪功能
     * @param args 命令行参数（未使用）
     */
/*    public static void main(String[] args) {
        // 输入输出文件路径（替换为实际路径）
        String inputPath = "D:\\work\\云平台\\样例数据-检验\\西北太海温\\NMF_BEN_OSMTSCB_NWPACDT_2025090812_168h_SWT.nc";
        String outputPath = "D:\\work\\NMF_BEN_OSMTSCB_NWPACDT_2025090812_168h_SWT_cropped.nc";

        // 执行裁剪
        try {
            cropNcFileByOffshoreRange(inputPath, outputPath);
            System.out.println("裁剪完成！新文件路径：" + outputPath);
        } catch (Exception e) {
            System.err.println("裁剪失败：" + e.getMessage());
            e.printStackTrace();
        }
    }*/

    /**
     * 根据中国近海范围裁剪NC文件（适配4维数据[time, depth, lat, lon]）
     * @param inputPath  输入NC文件路径
     * @param outputPath 输出NC文件路径
     * @throws IOException        文件操作异常
     * @throws InvalidRangeException 数据范围异常
     */
    public static boolean cropNcFileByOffshoreRange(String inputPath, String outputPath)
            throws IOException, InvalidRangeException {
        NetcdfFile inputFile = null;
        NetcdfFileWriter writer = null;

        try {
            // 1. 打开输入文件
            inputFile = NetcdfFile.open(inputPath);
            System.out.println("成功打开输入文件：" + inputPath);

            // 2. 获取关键变量（匹配4维数据结构，新增depth变量）
            Variable latVar = getVariable(inputFile, "lat", "纬度");
            Variable lonVar = getVariable(inputFile, "lon", "经度");
            Variable dataVar = getVariable(inputFile, "temp", "海温");
            Variable timeVar = getVariable(inputFile, "time", "时间"); // 4维中第1维（time）
            Variable depthVar = getVariable(inputFile, "z", "深度"); // 4维中第2维（depth）

            // 3. 校验经纬度数据类型（必须为float）
            validateVariableType(latVar, DataType.FLOAT, "纬度");
            validateVariableType(lonVar, DataType.FLOAT, "经度");

            // 4. 读取经纬度数据
            float[] lats = readFloatVariable(latVar);
            float[] lons = readFloatVariable(lonVar);

            // 5. 计算裁剪索引（仅裁剪lat和lon，time和depth全取）
            int[] latIndices = calculateRangeIndices(lats, MIN_LAT, MAX_LAT, "纬度");
            int[] lonIndices = calculateRangeIndices(lons, MIN_LON, MAX_LON, "经度");
            int latStart = latIndices[0], latCount = latIndices[1];
            int lonStart = lonIndices[0], lonCount = lonIndices[1];

            // 6. 裁剪数据（适配4维结构）
            Array croppedLat = cropVariable(latVar, new int[]{latStart}, new int[]{latCount});
            Array croppedLon = cropVariable(lonVar, new int[]{lonStart}, new int[]{lonCount});
            Array croppedDepth = cropVariable(depthVar, new int[]{0}, new int[]{depthVar.getShape()[0]}); // depth全取
            Array croppedTime = cropVariable(timeVar, new int[]{0}, new int[]{timeVar.getShape()[0]}); // time全取
            Array croppedData = cropDataVariable(dataVar, latStart, latCount, lonStart, lonCount);

            // 7. 创建并写入新文件（适配4维输出）
            writer = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf4, outputPath);
            createOutputFile(writer, inputFile, latVar, lonVar, timeVar, depthVar, dataVar,
                    croppedLat, croppedLon, croppedTime, croppedDepth, croppedData, latCount, lonCount);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }finally {
            // 关闭资源
            if (inputFile != null) inputFile.close();
            if (writer != null) writer.close();
        }
    }

    /**
     * 裁剪数据变量（核心适配4维：[time, depth, lat, lon]）
     * 维度索引：0=time，1=depth，2=lat，3=lon（匹配shape[169,1,1084,1098]）
     */
    private static Array cropDataVariable(Variable dataVar,
                                          int latStart, int latCount,
                                          int lonStart, int lonCount)
            throws IOException, InvalidRangeException {
        // 验证数据变量是否为4维（匹配[time, depth, lat, lon]）
        if (dataVar.getShape().length != 4) {
            throw new IllegalArgumentException("数据变量维度数量错误，预期4维[time, depth, lat, lon]，实际" +
                    dataVar.getShape().length + "维");
        }

        // 定义4维裁剪规则：time（0）和depth（1）全取，仅裁剪lat（2）和lon（3）
        int[] origin = new int[]{0, 0, latStart, lonStart}; // 各维度起始索引
        int[] shape = new int[]{
                dataVar.getShape()[0], // time长度（169）
                dataVar.getShape()[1], // depth长度（1）
                latCount, // 裁剪后lat长度
                lonCount  // 裁剪后lon长度
        };

        // 校验裁剪范围是否越界（避免之前的越界错误）
        if (latStart + latCount - 1 >= dataVar.getShape()[2]) {
            throw new InvalidRangeException("lat维度（索引2）裁剪越界！最大索引" + (dataVar.getShape()[2]-1) +
                    "，请求结束索引" + (latStart + latCount - 1));
        }
        if (lonStart + lonCount - 1 >= dataVar.getShape()[3]) {
            throw new InvalidRangeException("lon维度（索引3）裁剪越界！最大索引" + (dataVar.getShape()[3]-1) +
                    "，请求结束索引" + (lonStart + lonCount - 1));
        }

        return cropVariable(dataVar, origin, shape);
    }

    /**
     * 创建输出文件（适配4维数据结构）
     */
    private static void createOutputFile(NetcdfFileWriter writer, NetcdfFile inputFile,
                                         Variable latVar, Variable lonVar, Variable timeVar, Variable depthVar, Variable dataVar,
                                         Array croppedLat, Array croppedLon, Array croppedTime, Array croppedDepth, Array croppedData,
                                         int newLatSize, int newLonSize)
            throws IOException, InvalidRangeException {

        // 1. 创建新维度（time和depth长度不变，仅修改lat和lon）
        Dimension timeDim = writer.addDimension(null, timeVar.getDimensions().get(0).getName(), timeVar.getShape()[0]);
        Dimension depthDim = writer.addDimension(null, depthVar.getDimensions().get(0).getName(), depthVar.getShape()[0]);
        Dimension latDim = writer.addDimension(null, latVar.getDimensions().get(0).getName(), newLatSize);
        Dimension lonDim = writer.addDimension(null, lonVar.getDimensions().get(0).getName(), newLonSize);

        // 2. 定义4维数据变量的维度顺序（与原文件一致：[time, depth, lat, lon]）
        List<Dimension> dataDims = List.of(timeDim, depthDim, latDim, lonDim);

        // 3. 创建新变量（匹配原变量类型和维度）
        Variable newTimeVar = writer.addVariable(null, timeVar.getShortName(), timeVar.getDataType(), timeDim.getName());
        Variable newDepthVar = writer.addVariable(null, depthVar.getShortName(), depthVar.getDataType(), depthDim.getName());
        Variable newLatVar = writer.addVariable(null, latVar.getShortName(), DataType.FLOAT, latDim.getName());
        Variable newLonVar = writer.addVariable(null, lonVar.getShortName(), DataType.FLOAT, lonDim.getName());
        Variable newDataVar = writer.addVariable(null, dataVar.getShortName(), dataVar.getDataType(), dataDims);

        // 4. 复制变量属性（保留元数据，修复属性复制兼容性）
        copyVariableAttributes(latVar, newLatVar);
        copyVariableAttributes(lonVar, newLonVar);
        copyVariableAttributes(timeVar, newTimeVar);
        copyVariableAttributes(depthVar, newDepthVar);
        copyVariableAttributes(dataVar, newDataVar);

        // 5. 复制全局属性
        for (Attribute attr : inputFile.getGlobalAttributes()) {
            writer.addGlobalAttribute(attr);
        }

        // 6. 写入裁剪后的数据
        writer.create();
        writer.write(newTimeVar, croppedTime);
        writer.write(newDepthVar, croppedDepth);
        writer.write(newLatVar, croppedLat);
        writer.write(newLonVar, croppedLon);
        writer.write(newDataVar, croppedData);

        // 打印新文件维度信息（验证结果）
        System.out.println("新文件创建成功，维度信息：");
        System.out.println("时间（time）：" + timeDim.getLength() + "个时次");
        System.out.println("深度（depth）：" + depthDim.getLength() + "层");
        System.out.println("纬度（lat）：" + newLatSize + "个点");
        System.out.println("经度（lon）：" + newLonSize + "个点");
        System.out.println("数据变量维度顺序：[time, depth, lat, lon]");
    }

    // ---------------------- 以下辅助方法保持不变，但优化兼容性 ----------------------
    private static Variable getVariable(NetcdfFile file, String varName, String varDesc) throws IOException {
        Variable var = file.findVariable(varName);
        if (var == null) {
            throw new IOException("文件中不存在" + varDesc + "变量（变量名：" + varName + "），请检查变量名是否正确");
        }
        return var;
    }

    private static void validateVariableType(Variable var, DataType expected, String varDesc) {
        if (var.getDataType() != expected) {
            throw new IllegalArgumentException(varDesc + "变量数据类型错误，期望：" + expected +
                    "，实际：" + var.getDataType());
        }
    }

    private static float[] readFloatVariable(Variable var) throws IOException {
        Array array = var.read();
        return (float[]) array.copyTo1DJavaArray();
    }

    private static int[] calculateRangeIndices(float[] data, double minVal, double maxVal, String dataDesc) {
        if (data == null || data.length == 0) {
            throw new IllegalArgumentException(dataDesc + "数据为空");
        }

        boolean isAscending = data.length > 1 && data[data.length - 1] > data[0];
        int start = -1, end = -1;
        for (int i = 0; i < data.length; i++) {
            double val = data[i];
            boolean inRange = isAscending ? (val >= minVal && val <= maxVal) : (val <= minVal && val >= maxVal);
            if (inRange) {
                if (start == -1) start = i;
                end = i;
            }
        }

        if (start == -1 || end == -1) {
            throw new IllegalArgumentException(dataDesc + "数据中未找到符合范围[" + minVal + "," + maxVal + "]的数值");
        }
        return new int[]{start, end - start + 1};
    }

    private static Array cropVariable(Variable var, int[] origin, int[] shape)
            throws IOException, InvalidRangeException {
        if (origin.length != var.getShape().length) {
            throw new IllegalArgumentException("起始索引维度（" + origin.length + "）与变量维度（" + var.getShape().length + "）不匹配");
        }

        Range[] ranges = new Range[origin.length];
        for (int i = 0; i < origin.length; i++) {
            int end = origin[i] + shape[i] - 1;
            if (end >= var.getShape()[i]) {
                throw new InvalidRangeException("裁剪范围超出变量边界，维度" + i + "：" + end + " >= " + var.getShape()[i]);
            }
            ranges[i] = new Range(origin[i], end);
        }
        return var.read(Arrays.asList(ranges));
    }

    /**
     * 修复属性复制：用getValues()获取属性值，适配各版本NetCDF
     */
    private static void copyVariableAttributes(Variable src, Variable dest) {
        for (Attribute attr : src.getAttributes()) {
            // 通过getValues()获取属性值数组（兼容无参getValue()不存在的问题）
            Array attrValues = attr.getValues();
            // 单值属性取第一个元素，多值属性直接用数组（覆盖多数场景）
            Object attrValue = attrValues.getSize() == 1 ? attrValues.getObject(0) : attrValues;
            dest.addAttribute(new Attribute(attr.getName(), attrValues));
        }
    }
}